/*
 * This file is part of JFlowMap.
 *
 * Copyright 2009 Ilya Boyandin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jflowmap.views;

import java.awt.Font;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.util.List;

import jflowmap.util.piccolo.PButton;
import jflowmap.util.piccolo.PNodes;

import com.google.common.collect.Lists;

import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PPaintContext;

/**
 * @author Ilya Boyandin
 */
public class Legend extends PPath {

	private static final long serialVersionUID = -8907313603307434727L;

	static final Font LABEL_FONT = new Font("Arial", Font.BOLD, 8);

	private final double gapBetweenLines = 2, gapBeforeText = 5;
	private final double gapAfterHeader = 4;
	private final double paddingX = 4, paddingY = 2;
	private final double startY = 8;
	private final double startX = 8;

	private ItemProducer itemsProducer;

	private final Paint textPaint;

	private PButton hideButton;
	private PBasicInputEventHandler inputEventHandler;

	public Legend(Paint paint, Paint textPaint, ItemProducer itemsProducer) {
		super(new RoundRectangle2D.Double(4,4,140,250,10,20));

		this.itemsProducer = itemsProducer;
		this.textPaint = textPaint;
		setPaint(paint);
		setStroke(null);
		setStrokePaint(null);
		setPickable(false);
		update();
	}

	public ItemProducer getItemsProducer() {
		return itemsProducer;
	}

	public void setItemsProducer(ItemProducer itemsProducer) {
		this.itemsProducer = itemsProducer;
	}

	public interface ItemProducer {
		PNode createHeader();

		Iterable<PNode> createItems();
	}


	public void update() {
		removeAllChildren();

		PNode header = itemsProducer.createHeader();

		final double posX = startX + paddingX;
		double posY = startY + paddingY;

		double headerWidth, headerHeight;
		if (header != null) {
			addChild(header);
			PNodes.setPosition(header, posX, posY);
			PBounds headerBounds = header.getFullBoundsReference();
			headerWidth = headerBounds.getWidth();
			headerHeight = headerBounds.getHeight();
			posY += headerHeight + paddingY + gapAfterHeader;
		} else {
			headerWidth = headerHeight = 0;
		}


		double maxItemWidth = Double.NaN;
		double maxLabelX = Double.NaN;
		PText labelNode = null;
		double rightMost = 0;
		int count = 0;

		Iterable<PNode> itemNodes = itemsProducer.createItems();

		List<PText> labelNodes = Lists.newArrayList();

		for (PNode item: itemNodes) {
			addChild(item);
			PNodes.setPosition(item, posX, posY);

			PBounds itemBounds = item.getFullBoundsReference();
			double itemHeight = itemBounds.getHeight();
			double itemWidth = itemBounds.getWidth();
			if (Double.isNaN(maxItemWidth)  ||  maxItemWidth < itemWidth) {
				maxItemWidth = itemWidth;
			}


			double labelX = posX + itemBounds.getWidth() + gapBeforeText;
			if (Double.isNaN(maxLabelX)  ||  labelX > maxLabelX) {
				maxLabelX = labelX;
			}
			labelNode = createLabel(item.getName(), labelX, 0);
			labelNodes.add(labelNode);
			addChild(labelNode);


			double labelHeight = labelNode.getFullBoundsReference().getHeight();
			double height = Math.max(itemHeight, labelHeight) + gapBetweenLines;

			labelNode.setY(item.getY() + (itemHeight - labelHeight)/2);

			double r = labelNode.getX() + labelNode.getWidth();
			if (r > rightMost) {
				rightMost = r;
			}

			posY += height;

			count++;
		}

		if (count > 0) {
			posY -= gapBetweenLines;
			for (PText label : labelNodes) {
				label.setX(maxLabelX);
			}

			for (PNode itemNode : itemNodes) {
				itemNode.setX(posX + (maxItemWidth - itemNode.getWidth())/2);
			}
		}
		setWidth(Math.max(headerWidth, rightMost - getY()) + paddingX * 2);
		setHeight(posY);// - gapBetweenLines + paddingY * 2);

		if (this.hideButton != null) {
			this.removeChild(hideButton);
		}

		hideButton = new PButton("<", false, new Font("Arial", Font.BOLD, 16));
		hideButton.setX(this.getWidth() + 10);
		hideButton.setY(posY - hideButton.getHeight() - 5);
		addChild(hideButton);

		this.inputEventHandler = new PBasicInputEventHandler() {
			private boolean hidden = false;
			private double oldPosX = Legend.this.getX() + Legend.this.getOffset().getX();

			@Override
			public void mouseClicked(PInputEvent event) {
				if (hidden) {
					Legend.this.animateToPositionScaleRotation(oldPosX,
							Legend.this.getOffset().getY(), 1.0, 0.0, 1000);
					hidden = false;
					hideButton.setText("<");
				} else {
					Legend.this.animateToPositionScaleRotation(0.5*(-Legend.this.getWidth()),
							Legend.this.getOffset().getY(), 0.5, 0.0, 1000);
					hidden = true;
					hideButton.setText(">");
				}

			}
		};
		hideButton.addInputEventListener(this.inputEventHandler);
		
		this.moveToFront();
	}


	private PText createLabel(String itemText, double leftX, double middleY) {
		PText ptext = new PText(itemText);
		ptext.setX(leftX);
		ptext.setY(middleY - Legend.LABEL_FONT.getSize2D()/2 - 1);
		ptext.setFont(Legend.LABEL_FONT);
		ptext.setTextPaint(textPaint);
		//    ptext.setPaint(Color.black);
		//    ptext.setTextPaint(Color.white);
		//    ptext.setJustification(JLabel.CENTER_ALIGNMENT);
		return ptext;
	}

	@Override
	public void paint(PPaintContext pc) {
		final int oldQuality = pc.getRenderQuality();
		if (oldQuality != PPaintContext.HIGH_QUALITY_RENDERING) {
			pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
		}

		super.paint(pc);

		if (oldQuality != PPaintContext.HIGH_QUALITY_RENDERING) {
			pc.setRenderQuality(oldQuality);
		}
	}


}
